package com.myecl.poker.state;


import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;

import com.myecl.poker.communication.ViewData;
import com.myecl.poker.option.Command;
import com.myecl.poker.option.CommandValue;
import com.myecl.poker.rules.Card;
import com.myecl.poker.rules.enums.BettingRound;
import com.myecl.poker.rules.enums.Option;
import com.myecl.poker.util.Logging;
import com.myecl.poker.util.Shuffle;


public class Round {
	
	private List<Card> dealerCards;
	private List<Card> communityCards = new LinkedList<Card>();
	
	private Queue<Player> players = new LinkedList<Player>();;
	private Player button;
	private Player roundEnd;
	
	private Queue<BettingRound> bettingRound = new PriorityQueue<BettingRound>();
	
	private Map<Player, Double> playersBids = new HashMap<Player, Double>();	
	private double pot;
	private double bet;

	
	public Round(List<Player> players, List<Card> dealerCards, Player button) {
		assert players.size() > 1;
		this.dealerCards = dealerCards;
		this.button = button;
		for(Player p : players) {
			playersBids.put(p, -1.0);
			this.players.add(p);
		}
		for(BettingRound b: BettingRound.values()) {
			bettingRound.add(b);
		}
		deal();
		blinds(200.00, 400.00);
		roundEnd = nextPlayer();
	}

	public Player nextPlayer() {
		return players.peek();
	}
	
	private void deal() {
		Shuffle.rotateTo(players, button, 1);
		for(Player p : players) {
			p.setCards(Shuffle.deal(dealerCards, 2));
		}
		updateViews();
	}
	
	public List<Card> getComCards() {
		return communityCards;
	}
//	
	public void execute(List<Command> commands) {
		List<Option> options = getAvailableOptions();
		for (Command c: commands) {
			if(options.contains(c.getOption())) {
				execute(c);
				commands.clear();
			}
		}
	}
	
	private void execute(Command command) {
		Player player = nextPlayer();
		Logging.trace(player.getName() + ": " + command.getOption().toString());
		switch(command.getOption()) {
			case FOLD: 
				players.remove();
				playersBids.put(player, 0.0);
				break;	
			
			case CHECK: 
				break;
			
			case CALL: 
				CommandValue commandValue = (CommandValue) command; 
				if (commandValue.getValue() == bet) {
					playersBids.put(player, bet);
				}
				break;
			
			case RAISE: 
				commandValue = (CommandValue) command; 
				if (commandValue.getValue() > bet) {
					playersBids.put(player, commandValue.getValue());
					bet = commandValue.getValue();
					roundEnd = nextPlayer();
				}
				break;
	}
	rotatePlayers();
	if (players.peek().equals(roundEnd)) {
		bettingRound.remove();
		if(!bettingRound.isEmpty()) {
			Shuffle.rotateTo(players, button, 1);
			roundEnd = players.peek();
		} else {
			
		}
	}
	Logging.trace("Betting Round: " + bettingRound.peek());
	updateViews();
	}
	
	private List<Option> getAvailableOptions() {
		List<Option> result = new LinkedList<Option>();
		result.add(Option.FOLD);
		result.add(Option.RAISE);
		Player current = nextPlayer();
		if (bet == playersBids.get(current)) {
			result.add(Option.CHECK);
		}
		if (bet > playersBids.get(current)) {
			result.add(Option.CALL);
		}
		return result;
	}
	 
	
	private void rotatePlayers() {
		do {
			Shuffle.rotate(players, 1);
		} while (!!players.peek().equals(roundEnd) && playersBids.get(players.peek()) == 0);
	}
	
	private boolean bettingRoundEnd(Command command) {
		boolean result = false;
		if (bettingRound.peek() == BettingRound.PRE_FLOP) {
			
		} else {
			if (playersBids.get(players.peek()) == bet ) {
				result = true;
			}
		}
		return result;
	}
	
	private void blinds(double smallBlind, double bigBlind)  {
		Shuffle.rotateTo(players, button, 1);
		bet = bigBlind;
		playersBids.put(players.peek(), players.peek().bid(smallBlind));
		Shuffle.rotate(players, 1);
		playersBids.put(players.peek(), players.peek().bid(bigBlind));
		Shuffle.rotate(players, 1);
	}
	
	private void updateViews() {
		for (Player p: players) {
			p.sendViewData(new ViewData(
				communityCards, p.getCards(), true, true, true, true));
		}
	}

	
}















